home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Add-Ons / After Dark / ScreenFlip 1.0 / Source / ScreenFlip.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-21  |  20.9 KB  |  649 lines  |  [TEXT/R*ch]

  1. /*
  2.  *    ScreenFlip 1.0 — a screensaver module by Leo Breebaart, Kronto Software 1994.
  3.  *
  4.  *      For non-technical information about this module and for the credits,
  5.  *    see the README file.
  6.  *       
  7.  *       This module keeps flipping your screen’s contents horiontally and vertically. 
  8.  *       The flips can be instantaneous or ‘animated’ column/row-wise.
  9.  *       
  10.  *       I have commented the code in a way that experienced programmers may find
  11.  *       overkill, but it was done in the hope that beginning After Dark programmers
  12.  *       will find ScreenFlip a useful starting point for writing their own 
  13.  *       modules. If you want to look at some more documented code, check out The Swarm, 
  14.  *      another freeware screensaver module I wrote.
  15.  *       
  16.  *       For general information about how to write an After Dark-compatible module, 
  17.  *       see the After Dark Programmer’s Manual, but make sure you have the most recent
  18.  *       version (released with After Dark 2.0u and later). The comments in this
  19.  *    module do assume you are at least *aware* of the basic After Dark mechanisms.
  20.  *
  21.  *      Here we go...
  22.  */
  23.  
  24. #include <QDoffscreen.h>
  25. #include "AfterDarkTypes.h"
  26.  
  27.     // Flipping the screen is implemented as a kind of rudimentary state machine.
  28.     // At any time the module is in one of these four FlipStates. HORIZONTAL and
  29.     // VERTICAL mean we're in the processing of doing a horizontal resp. vertical
  30.     // screen flip. SWITCH means we've just completed one entire flip, and will now
  31.     // determine what the next flip will be. NONE means we are in a state of rest
  32.     // between flips.
  33. typedef enum { HORIZONTAL, VERTICAL, SWITCH, NONE } FlipStates;
  34.  
  35.     // This is a secondary state, which controls the direction the ripples of
  36.     // the current animation are moving in (for the VERTICAL and HORIZONTAL flip states.
  37. typedef enum { INWARDS, OUTWARDS } DirectionStates;
  38.  
  39.  
  40.     // QuickDraw color constants used throughout the program.
  41. RGBColor whiteRGB = { 0xFFFF, 0xFFFF, 0xFFFF };
  42. RGBColor blackRGB = { 0, 0, 0 };
  43.  
  44.  
  45. // In the following data structure most two-element arrays store information
  46. // pertaining horizontal flips in the first element, and the same information
  47. // for the vertical case in the second element. This makes it possible to
  48. // write things like 'bufworld[VERTICAL]' or 'maxStep[HORIZONTAL]', which is
  49. // not only very clear, but also allows us to eliminate a lot of code duplication.
  50.  
  51. typedef struct FlipStorage        // All data we use is stored in this struct.
  52. {                
  53.     GWorldPtr bufWorld[2];        // Small buffers for storing a row resp. column of pixels.
  54.     GWorldPtr offWorld;            // Large buffer for storing a copy of the entire screen.
  55.     
  56.     PixMapHandle screenMap;        // Bitmap for an entire screen.
  57.     PixMapHandle bufMap[2];        // Bitmaps associated with the bufWorld array.
  58.     
  59.     Rect strip[2];                // Which two columns/rows to swap this step.
  60.     Rect bufRect[2];            // Bounding rectangles associated with bufWorld.
  61.     Rect r;                        // Bounding rectangle of the screen.
  62.     
  63.     FlipStates movement;        // Current flip state.
  64.     DirectionStates direction;    // Current ripple movement.
  65.     int t;                        // The current step (each steps, two columns/rows get switched).
  66.  
  67.     long startTick, delay;        // Variables for keeping track of delays between flips.
  68.     int maxStep[2];                // After this many steps before flip is finished.
  69.  
  70.     Boolean demoMode;            // Are we currently in After Dark demo mode?
  71.     Boolean instantFlip;        // Corresponds to check box in module interface.
  72. }
  73. FlipStorage, *FlipStoragePtr;
  74.  
  75.  
  76.     // After Dark's way of showing an error to the user.
  77.     // '##' is an ANSI-ism meaning meta-concatenation.
  78. #define ErrorMsg(m) BlockMove(CtoPstr(m), params->errorMessage, 1 + m##[0]);
  79.  
  80.     // Let's be good ANSI citizens, and define some function prototypes.
  81.     // These functions will be called from the AfterDarkShell.c code. 
  82. OSErr DoInitialize(Handle *storage, RgnHandle blankRgn, GMParamBlockPtr params);
  83. OSErr DoClose(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params);
  84. OSErr DoBlank(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params);
  85. OSErr DoDrawFrame(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params);
  86. OSErr DoHelp(RgnHandle blankRgn, GMParamBlockPtr params);
  87.  
  88.     // Local functions.
  89. OSErr    AboutBoxError(Rect graf);
  90. int     RangedRdm(int min, int max);
  91. Handle    BestNewHandle(Size s);
  92. void     SetRects(FlipStoragePtr fs, int movement);
  93. void    SwapStrips(FlipStoragePtr fs, int movement);
  94. OSErr    InitStructures(FlipStoragePtr fs, int movement);
  95.  
  96.  
  97. GWorldPtr        currPort;    // The 'real' screen consists of two components
  98. GDHandle        currDev;    // which we'll save in these global variables.
  99. PixMapHandle    realMap;    // And this is the associated BitMap.
  100.  
  101.  
  102.     // DoInitialize allocates the Flip data structure as defined
  103.     // above, initializes the variables in that structure, and checks
  104.     // for possible problems.
  105.  
  106. OSErr
  107. DoInitialize(Handle *storage, RgnHandle blankRgn, GMParamBlockPtr params)
  108. {
  109.     FlipStoragePtr fs;        // The flip structure, obviously.
  110.     RgnHandle tmpRgn = NewRgn();
  111.  
  112.          // Our offscreen graphics worlds need Color QuickDraw.
  113.     if (!params->colorQDAvail)
  114.     {
  115.         DisposHandle(*storage);
  116.         ErrorMsg("ScreenFlip:  Sorry, I need Color QuickDraw to run!");
  117.         return ModuleError;
  118.     }
  119.  
  120.         // Allocate 'master' handle to the storage struct.
  121.     if ((*storage = BestNewHandle(sizeof(FlipStorage))) == NULL)
  122.     {     
  123.         ErrorMsg("ScreenFlip:  Couldn't allocate enough memory!");
  124.         return ModuleError;
  125.     }
  126.  
  127.         // Lock down the storage so we can refer to it by pointer. 
  128.     HLockHi(*storage);
  129.     fs = (FlipStoragePtr) **storage;
  130.     
  131.         // Get the delay-between-flips value from the slider in the module interface
  132.         // and the instantFlip boolean from the checkbox.
  133.     fs->delay = params->controlValues[0];
  134.     fs->instantFlip = params->controlValues[1];
  135.  
  136.         // Sanity check. This should never happen.
  137.     if (fs->delay < 0 || fs->delay > 100)
  138.     {
  139.             ErrorMsg("ScreenFlip:  Internal Error — insane delay value!");
  140.             return ModuleError;
  141.     }
  142.  
  143.         // Initialize the random number generator.
  144.     params->qdGlobalsCopy->qdRandSeed = TickCount();
  145.     fs->demoMode = !EmptyRect((¶ms->demoRect));
  146.  
  147.         // Initialize the globals variables that describe the
  148.         // real, 'physical' screen.
  149.     GetGWorld(&currPort,&currDev);
  150.     realMap = GetGWorldPixMap((GWorldPtr) currPort);
  151.  
  152.         // The CopyBits function expect these back- and foreground values.
  153.     RGBBackColor(&whiteRGB);
  154.     RGBForeColor(&blackRGB);
  155.  
  156.         // Find the screen's bounding rectangle, as well as the coordinates 
  157.         // of a single column and row.
  158.     fs->r = params->monitors->monitorList[0].bounds;    
  159.     SetRect(&(fs->bufRect[VERTICAL]), 0, 0, 1, fs->r.bottom);
  160.     SetRect(&(fs->bufRect[HORIZONTAL]), 0, 0, fs->r.right, 1);
  161.  
  162.         // Initialize data structures for row/column buffers.
  163.     (void) InitStructures(fs, VERTICAL);
  164.     (void) InitStructures(fs, HORIZONTAL);
  165.     
  166.     fs->maxStep[VERTICAL] = fs->r.right / 2;
  167.     fs->maxStep[HORIZONTAL] = fs->r.bottom / 2;
  168.         
  169.         // We start our flipping in a random direction, moving either
  170.         // inwards or outwards, at time (or step) t = 0.    
  171.     fs->movement  = RangedRdm(0,1);
  172.     fs->direction = RangedRdm(0,1);
  173.     fs->t = 0;
  174.     
  175.         // If instantFlip is true, we do not show the animation 'ripples' on the
  176.         // screen (caused by successive swaps of pairs of rows/columns), but instead
  177.         // perform the swapping in an offscreen buffer copy of the entire screen, 
  178.         // which we blit onto the 'real' screen when the flip is completed.
  179.     if (fs->instantFlip)
  180.     {
  181.         if (NewGWorld(&(fs->offWorld), 0, &(fs->r), nil, nil, noNewDevice+useTempMem) != noErr)      
  182.         {     
  183.             DoClose(*storage, (RgnHandle) nil, (GMParamBlockPtr) nil);
  184.             ErrorMsg("ScreenFlip:  Not enough memory for offscreen graphics world!");
  185.             return ModuleError;            
  186.         }
  187.         fs->screenMap = GetGWorldPixMap((GWorldPtr) fs->offWorld);
  188.         
  189.             // Copy the real screen contents to the offscreen pixmap.
  190.         CopyBits((BitMap *) (*realMap), (BitMap *) (*fs->screenMap), 
  191.                   &fs->r, &fs->r,
  192.                   srcCopy, nil);
  193.     }
  194.     else
  195.             // If we do want ripple animation, the algorithm stays exactly the
  196.             // same, only we make screenMap refer to the physical screen, so that
  197.             // any changes to it will be reflected on the monitor immediately,
  198.             // thus causing the ripples to appear.
  199.         fs->screenMap = realMap;
  200.             
  201.     return noErr;
  202. }
  203.  
  204.  
  205.     // Initialize small offscreen buffers for storing a column or row.
  206.  
  207. OSErr
  208. InitStructures (FlipStoragePtr fs, int movement)
  209. {
  210.      if (NewGWorld(&fs->bufWorld[movement], 0, &fs->bufRect[movement], nil, nil, noNewDevice+useTempMem) != noErr)      
  211.         return ModuleError;
  212.  
  213.     fs->bufMap[movement] = GetGWorldPixMap(fs->bufWorld[movement]);
  214.     
  215.     return noErr;
  216. }
  217.  
  218.  
  219.     // The DoBlank function blanks out all available screens, *except* for the
  220.     // main screen. If we are in 'instantFlip' mode, care should be taken to
  221.     // switch to the 'real' screen world (and back to offscreen afterwards) before blanking.
  222.  
  223. OSErr
  224. DoBlank(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params)
  225. {
  226.     RgnHandle otherScreens, mainScreenRgn;
  227.     Rect r = params->monitors->monitorList[0].bounds;    
  228.     
  229.     FlipStoragePtr fs = (FlipStoragePtr) *storage;
  230.     
  231.         // If just one monitor, then we don't need to bother at all...
  232.     if (params->monitors->monitorCount != 1)
  233.     {        
  234.         otherScreens = NewRgn();
  235.         mainScreenRgn = NewRgn();
  236.         
  237.         RGBBackColor(&blackRGB);
  238.     
  239.         SetRectRgn(mainScreenRgn, r.left, r.top, r.right, r.bottom);
  240.         DiffRgn(blankRgn, mainScreenRgn, otherScreens);
  241.         EraseRgn(otherScreens);
  242.     
  243.             // In order for CopyBits calls to work, the destination
  244.             // world's backgroundworld *has* to be white (and the foreground
  245.             // black.
  246.         RGBBackColor(&whiteRGB);
  247.     
  248.         DisposeRgn(mainScreenRgn);
  249.         DisposeRgn(otherScreens);
  250.     }
  251.  
  252.     return noErr;
  253. }
  254.  
  255.  
  256.     // At any given step 't' of the animation process, this function
  257.     // calculates which two rows or columns are to be swapped, taking
  258.     // into account that the animation can move inwards or outwards.
  259.     
  260. void
  261. SetRects(FlipStoragePtr fs, int movement)
  262. {
  263.     int step;
  264.     
  265.     if (fs->direction == INWARDS)
  266.         step = fs->t;
  267.     else /* OUTWARDS */
  268.         step = fs->maxStep[movement] - fs->t - 1;
  269.     
  270.     if (movement == HORIZONTAL)
  271.     {
  272.         SetRect(&fs->strip[0], 0, step, fs->r.right, step+1);
  273.         SetRect(&fs->strip[1], 0, fs->r.bottom-step-1, fs->r.right, fs->r.bottom-step);
  274.     }
  275.     else /* VERTICAL */
  276.     {
  277.         SetRect(&fs->strip[0], step, 0, step+1, fs->r.bottom);
  278.         SetRect(&fs->strip[1], fs->r.right-step-1, 0, fs->r.right-step, fs->r.bottom);
  279.     }
  280. }
  281.  
  282.     
  283.     // The true bottleneck of this module. Three CopyBits calls are necessary to
  284.     // swap the two columns or rows calculated in SetRects. If you look closely
  285.     // at the parameters, you'll notice that these calls are just a bitmap-moving
  286.     // way of doing the well-known "temp = a; a = b; b = temp;" variable swap.
  287.     
  288. void 
  289. SwapStrips(FlipStoragePtr fs, int movement)
  290. {
  291.     CopyBits((BitMap *) (*fs->screenMap), (BitMap *) (*fs->bufMap[movement]), 
  292.               &fs->strip[0], &fs->bufRect[movement],
  293.               srcCopy, nil);
  294.     CopyBits((BitMap *) (*fs->screenMap), (BitMap *) (*fs->screenMap), 
  295.                 &fs->strip[1], &fs->strip[0],
  296.                 srcCopy, nil);
  297.     CopyBits((BitMap *) (*fs->bufMap[movement]), (BitMap *) (*fs->screenMap), 
  298.                 &fs->bufRect[movement], &fs->strip[1],
  299.                 srcCopy, nil);
  300. }
  301.  
  302.     // How many ticks have passed since we started counting?
  303. #define TicksPassed (TickCount() - fs->startTick)
  304.  
  305.     // The main animation routine. Each call to DoDrawFrame causes (when in 
  306.     // an animation state) two rows or columns to exchange places.
  307. OSErr
  308. DoDrawFrame(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params)
  309. {    
  310.     FlipStoragePtr fs = (FlipStoragePtr) *storage;
  311.     
  312.         // If we are in After Dark demo mode, the user may have changed
  313.         // the slider value, so we must re-read it.
  314.     if (fs->demoMode)
  315.     {
  316.         if (fs->delay != params->controlValues[0]) 
  317.             fs->delay = params->controlValues[0];
  318.     }
  319.  
  320.     switch (fs->movement)
  321.     {
  322.             
  323.         case HORIZONTAL:
  324.         case VERTICAL:
  325.     
  326.             if (fs->instantFlip)
  327.             {
  328.                     // If we are instant-flipping there is no need to swap just
  329.                     // one pair of columns/rows for each call of this function.
  330.                     // Instead, we can avoid many seconds of delay by flipping
  331.                     // the entire screen in one go. This of course at the price
  332.                     // of a slightly less responsive attitude towards module
  333.                     // interruptions -- we now spend much more time in DoDrawFrame.
  334.                     
  335.                     // Delay period includes offscreen drawing time.
  336.                 fs->startTick = TickCount();
  337.                 
  338.                 while (fs->t < fs->maxStep[fs->movement])
  339.                 {
  340.                     SetRects(fs, fs->movement);
  341.                     SwapStrips(fs, fs->movement);
  342.                     fs->t++;
  343.                 }
  344.             
  345.                     // Blit the completed flip to the real screen.
  346.                 CopyBits((BitMap *) (*fs->screenMap), (BitMap *) (*realMap), 
  347.                           &fs->r, &fs->r,
  348.                           srcCopy, nil);
  349.                 
  350.                     // Start of (possible) delay period.
  351.                 fs->movement = NONE;
  352.             }
  353.             else
  354.             {
  355.                 SetRects(fs, fs->movement);
  356.                 SwapStrips(fs, fs->movement);
  357.     
  358.                     // Increment time variable and test for stop criteria.
  359.                 if (++fs->t >= fs->maxStep[fs->movement])
  360.                 {    
  361.                         // Start (possible) delay period.
  362.                         // Delay period does *not* include on-screen drawing time.
  363.                     fs->movement = NONE;
  364.                     fs->startTick = TickCount();
  365.                 }
  366.             }
  367.             break;
  368.  
  369.         case NONE:
  370.         
  371.                 // Do nothing until user-specified delay has passed.
  372.             if (TicksPassed < 10 * (fs->delay / 25) * 60)
  373.                 return noErr;
  374.             else
  375.                 fs->movement = SWITCH;
  376.             break;
  377.                 
  378.         case SWITCH:
  379.                 // Change to a random new flip type.
  380.             fs->movement = RangedRdm(0,1);
  381.             
  382.                 // Animation direction always goes in-out-in-out-in-out...
  383.             if (fs->direction == INWARDS)
  384.                 fs->direction = OUTWARDS;
  385.             else
  386.                 fs->direction = INWARDS;
  387.                 
  388.                 // Re-initialize time variable.
  389.             fs->t = 0;
  390.             break;
  391.     }
  392.     return noErr;
  393. }
  394.  
  395.  
  396.     // ScreenFlip features a funky About Box, which has a miniature logo flip
  397.     // going on inside of it. Creating that animation is mostly straightforward: I
  398.     // just call all the previous functions, i.e. DoInitialize,
  399.     // DoBlank, and DoDrawFrame -- and that's it. The tricky part lies in getting
  400.     // some correct variables in place, and setting up the right graphics port.
  401.     //
  402.     // One thing you should realize about DoHelp: when After Dark calls this
  403.     // function, it will already have set the currPort and the blankRgn to the help rectangle. 
  404.     // So you are at this point no longer drawing to the entire screen.
  405.     //
  406.     // Final note: if you want to use a DoHelp function yourself, realize that (a) 
  407.     // you need to tell After Dark you want to 'take over' (see the Cals resource in
  408.     // the Programmer's Manual), and (b) 'storage' is *not* initialized in DoHelp,
  409.     // because After Dark calls DoHelp without calling DoInitialize first, in contrast
  410.     // to e.g. DoDrawFrame. Unfortunately, a lot of code shows example 'DoAbout' or
  411.     // 'DoHelp' functions with the 'storage' handle as a parameter. But this parameter
  412.     // will *not* be intialized, and if you use it, you will crash horribly.
  413.  
  414. OSErr
  415. DoHelp(RgnHandle blankRgn, GMParamBlockPtr params)
  416. {
  417.     FlipStorage **miniFlip;
  418.  
  419.     long dummy;
  420.     GrafPtr helpGraf;
  421.     short oldInstantFlip;
  422.     short oldCount;
  423.     Rect oldBounds;
  424.     RgnHandle miniBlankRgn = NewRgn();
  425.     
  426.        PicHandle helpPict;                
  427.     Rect picRect, helpRect, miniRect;
  428.         
  429.         // Get the real screen.
  430.     GetPort(&helpGraf);
  431.  
  432.         // Get the real screen rectangle. miniRect is the version
  433.         // we’ll use in the rest of this function, helpRect is the
  434.         // ‘original’ version we’ll need to pass to the error handling 
  435.         // routine. That last bit is somewhat of an ugly hack, and may be
  436.         // cleaned up in the next version.
  437.     helpRect = miniRect = helpGraf->portRect;
  438.     LocalToGlobal(&topLeft(helpRect));
  439.     LocalToGlobal(&botRight(helpRect));
  440.  
  441.         // Load the 'about' PICT resource.
  442.     if ((helpPict = GetPicture(2000)) == nil) 
  443.     {
  444.         ErrorMsg("ScreenFlip:  Couldn’t load PICT resource for help picture!");
  445.         return AboutBoxError(helpRect);
  446.     }
  447.     
  448.         // Draw the PICT, and release the resource.
  449.     picRect = (**helpPict).picFrame;
  450.     DrawPicture(helpPict, &picRect);
  451.     ReleaseResource((Handle) helpPict);
  452.  
  453.         // We now manually change the graphics environment
  454.         // from the entire help area to the subarea where we want the
  455.         // mini animation to take place (look at the about box in action if this
  456.         // is not clear to you). The current Port is moved and made smaller.
  457.         // The Toolbox calls take care of all the nasty details.
  458.     LocalToGlobal(&topLeft(miniRect));
  459.     LocalToGlobal(&botRight(miniRect));
  460.     MovePortTo(miniRect.left+24, miniRect.top+36);
  461.     PortSize(174, 89);
  462.     
  463.         // Save some relevant parameters that the 'real' After Dark
  464.         // animation will need to have restored later on.
  465.     oldCount  = params->monitors->monitorCount;
  466.     oldBounds = params->monitors->monitorList[0].bounds;
  467.     oldInstantFlip = params->controlValues[1];
  468.  
  469.         // Change the parameters temporarily to values appropriate for
  470.         // the miniFlip.
  471.     params->monitors->monitorCount = 1;
  472.     params->monitors->monitorList[0].bounds = helpGraf->portRect;
  473.     
  474.         // We want ripple effects regardless of current checkbox setting.
  475.     params->controlValues[1] = 0;
  476.     
  477.     RectRgn(miniBlankRgn, &helpGraf->portRect);    
  478.     
  479.         // Initialize the miniFlip struct. Note that we have a problem
  480.         // with error management here: I can use ErrorMsg all I want,
  481.         // but After Dark will do nothing with the return value of
  482.         // the DoHelp function, for some reason. So I have implemented
  483.         // a separate AboutBoxError() function of my own to handle
  484.         // errors.
  485.     if (DoInitialize((Handle *) &miniFlip, miniBlankRgn, params) != noErr) 
  486.     {
  487.         ErrorMsg("ScreenFlip:  Initialization of miniFlip failed!");
  488.         return AboutBoxError(helpRect);
  489.     }
  490.     
  491.         // Notice that DoBlank() does not need to be called for this miniFlip.
  492.     
  493.         // Wait for the user to release the mouse button, if necessary.
  494.     while (Button())
  495.         ;
  496.         
  497.         // Animate, until the user presses the mouse button.
  498.     while (!Button())
  499.     {
  500.         if (DoDrawFrame((Handle) miniFlip, miniBlankRgn, params) != noErr)
  501.         {
  502.             ErrorMsg("ScreenFlip:  DoDrawFrame of miniFlip failed!");
  503.             return AboutBoxError(helpRect);
  504.         }
  505.         
  506.             // MiniFlip animation is too fast!
  507.         Delay(1, &dummy);
  508.     }
  509.     
  510.         // Close it all up.
  511.     DoClose((Handle) miniFlip, miniBlankRgn, params);
  512.  
  513.         // Restore parameters.
  514.     params->monitors->monitorCount = oldCount;
  515.     params->monitors->monitorList[0].bounds = oldBounds;
  516.     params->controlValues[1] = oldInstantFlip;
  517.  
  518.     DisposeRgn(miniBlankRgn);
  519.     DisposeHandle((Handle) miniFlip);
  520.  
  521.      FlushEvents(everyEvent, 0);    
  522.  
  523.     return noErr;
  524. }
  525.     
  526.  
  527.     // The DoClose function merely disposes of all those handles and 
  528.     // offscreen worlds. Nothing interesting here.
  529.  
  530. OSErr
  531. DoClose(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params)
  532. {
  533.     FlipStorage **fs = (FlipStorage **) storage;
  534.     
  535.     if (fs)
  536.     {
  537.         DisposeGWorld((**fs).bufWorld[0]);
  538.         DisposeGWorld((**fs).bufWorld[1]);
  539.         if ((**fs).instantFlip)
  540.             DisposeGWorld((**fs).offWorld);
  541.  
  542.         DisposeHandle(storage);
  543.     }
  544.  
  545.     return noErr;
  546. }
  547.  
  548.  
  549.     // Random functions yield a number between min and max. The function
  550.     // originated from Think Reference, but this version is an adaptation
  551.     // by Joseph "Peek-a-Boo" Judge. 
  552.  
  553. int RangedRdm(int min, int max)
  554. {
  555.     unsigned    qdRdm;
  556.     long    range, t;
  557.     
  558.     qdRdm = Random();
  559.     range = (max - min) + 1;
  560.     t = ((long)qdRdm * range) / 65536;     // now 0 <= t <= range 
  561.     return( t+min );
  562. }
  563.  
  564.  
  565.     // This function tries to allocate a handle from temporary memory
  566.     // first, and only if that fails from the reserved memory.
  567.     // This function is taken from the DarkSide of the Mac example code.
  568.     // Note that the return value can be nil and should be checked.
  569.     
  570. Handle    
  571. BestNewHandle(Size s)
  572. {
  573.     Handle theHandle;
  574.     OSErr  anErr;
  575.     
  576.     if ((theHandle = TempNewHandle(s, &anErr)) == nil)
  577.         theHandle = NewHandle(s);
  578.         
  579.     return(theHandle);
  580. }
  581.  
  582.  
  583.     // Give some user feedback if anything goes wrong during the
  584.     // About Box animation.
  585.     
  586. OSErr
  587. AboutBoxError(Rect r)
  588. {    
  589.         // These tedious calls simply have the total effect of moving
  590.         // the drawing area back to exactly the part of the screen
  591.         // corresponding with the original About Box contents.
  592.         
  593.     Rect currRect = thePort->portRect;
  594.  
  595.     MovePortTo(r.left, r.top);
  596.     PortSize(r.right-r.left, r.bottom-r.top);
  597.  
  598.     ForeColor(blackColor);
  599.     BackColor(whiteColor);
  600.     
  601.     GlobalToLocal(&topLeft(r));
  602.     GlobalToLocal(&botRight(r));
  603.  
  604.     FillRect(&r, white);
  605.  
  606.     TextFont(geneva);
  607.     TextSize(9);
  608.  
  609.     MoveTo(15,20);
  610.      DrawString("\pI'm sorry — an error has occurred.");
  611.      MoveTo(15,30);
  612.      DrawString("\pNothing serious, mind you. The module");
  613.      MoveTo(15,40);
  614.      DrawString("\pprobably just ran out of memory.");
  615.      MoveTo(15,60);
  616.      DrawString("\pYou see, I have not implemented decent");
  617.      MoveTo(15,70);
  618.      DrawString("\perror management routines for this");
  619.      MoveTo(15,80);
  620.      DrawString("\pAbout Box yet. Next version, I promise.");
  621.      MoveTo(15,100);
  622.      DrawString("\pIf you *do* have lots of memory, but you");
  623.      MoveTo(15,110);
  624.      DrawString("\pstill see this message, then something");
  625.      MoveTo(15,120);
  626.      DrawString("\p*is* probably very wrong, and I would");
  627.      MoveTo(15,130);
  628.      DrawString("\preally appreciate an e-mail bug report.");
  629.      MoveTo(15,150);
  630.      DrawString("\pThanks!");
  631.     SysBeep(1); SysBeep(1);
  632.     
  633.     MovePortTo(currRect.left, currRect.top);
  634.     PortSize(currRect.right-currRect.left, currRect.bottom-currRect.top);
  635.      
  636.     while (Button())
  637.         ;
  638.      while (!Button())
  639.          ;
  640.      
  641.      FlushEvents(everyEvent, 0);    
  642.     return ModuleError;
  643. }
  644.  
  645. // This is THE END.
  646. // If you've learned anything from this code, or found errors in it, or
  647. // have questions about it, or whatever: feel free to drop me a note.
  648. // My e-mail address is: leo@cp.tn.tudelft.nl
  649.